//***********************************************************
// File uts_io.cpp - utility routines for Input/Output logic.
// To be used as IO proxy for all problem modules.

// Contains definitions of routines:
//
// Interface routines:
//   utr_io_read_mesh - to read mesh from given filename(s)

// Internal routines:
//  utr_io_read_nas - to read single .nas file
//  utr_io_read_in - to read single .in file
//  utr_io_read_dmp - to read single .dmp file
//  utr_io_read_kaz - to read single .kaz file
//  utr_io_read_am - to read single .am file
//  utr_io_read_dat - to read single .dat file
//  utr_io_read_jk - to read single .jk file
//----------------------------
// History:
//    Kazimierz.Michalik@agh.edu.pl - initial version
//
//*******************************************************

#ifndef _utr_io_intf_
#define _utr_io_intf_


// For file io.
#include <fstream>
// For portable filesystem
#include <boost/filesystem.hpp>
#include <boost/version.hpp>
// For portable regex
// (should be C++11 <regex>, but not yet supported)
#include <boost/regex.hpp>
// For not making all stuff again
#include <iterator>
#include <vector>
#include <algorithm>
#include <string>

using std::string;

// Includes:
#include "dbg.h"
#include "uth_intf.h"
#include "mmh_intf.h"
#include "pch_intf.h"

// Internal types
// pointer to a function to read/write specyfic file type.
typedef int (*mmpt_io_fptr)( // returns: numer of readed elements
	const int Mesh_field_id, // IN: mesh or field id
	const char * Filename);  // IN: filename to I/O operation

// Forward declarations of internal routines:
//---------------------------------------------------------
// utr_io_gather_filenames_from_dir - to write all filenames into given vector
//---------------------------------------------------------
void utr_io_gather_filenames_from_dir(
	const char * Dir, // IN: path of directory
	std::vector<string> & Filenames // OUT: vector of filenames in Dir
	);

//---------------------------------------------------------
// utr_io_filter_filename - to filter filenames with given regular expression
//---------------------------------------------------------
void utr_io_filter_filenames(
	std::vector<string>& Filenames, // IN: list of filenames to filter
							 //OUT: filtered filenames
	const char * Regex // IN: regular expression pattern to filter with
	);

//---------------------------------------------------------
// utr_io_get_reading_function - to return pointer to appropriate reading function
//  for given mesh type
//---------------------------------------------------------
mmpt_io_fptr utr_io_get_reading_function(// returns: pointer to read function for Mesh_type
										 const char Mesh_type // IN: type of mesh filename
										 );

//--------------------------------------------
//  utr_io_read_nas - to read single .nas file
//--------------------------------------------
int utr_io_read_nas(const int Mesh_id,const char* filename);
  
//--------------------------------------------
//  utr_io_read_in - to read single .in file
//--------------------------------------------
int utr_io_read_in(const int Mesh_id,const char* filename);

//--------------------------------------------
//  utr_io_read_binary - to read single binary dump file
//--------------------------------------------
int utr_io_read_binary(const int Mesh_id,const char* filename);

//--------------------------------------------
//  utr_io_read_hybrid - to read single .kaz file
//--------------------------------------------
int utr_io_read_hybrid(const int Mesh_id,const char* filename);

//--------------------------------------------
//  utr_io_read_am - to read single .am file
//--------------------------------------------
int utr_io_read_am(const int Mesh_id,const char* filename);

//--------------------------------------------
//  utr_io_read_dat - to read single .dat file
//--------------------------------------------
int utr_io_read_dat(const int Mesh_id,const char* filename);

//--------------------------------------------
//  utr_io_read_jk - to read single .jk file
//--------------------------------------------
int utr_io_read_jk(const int Mesh_id,const char* filename);

//--------------------------------------------
//  utr_io_read_vtk - to read single .vtk file
//--------------------------------------------
int utr_io_read_vtk(const int Mesh_id,const char* filename);

//--------------------------------------------
//  utr_io_write_vtk - to write single .vtk file
//--------------------------------------------
int utr_io_write_vtk(const int Mesh_id,const char* filename);

// Definitions:
#ifdef __cplusplus
extern "C"
{
#endif

//--------------------------------------------------------
//  utr_io_read_mesh - to read mesh with given filename(s)
//-------------------------------------------------------
//  Reads all mesh files from current working directory
//  that matches with passed regular expression Mesh_files_regex.
//  All files HAVE TO be the same type (ex. nas,dat,in etc.).
//  NOTE: single filename ex. "mesh.dat" is also a valid regular expression.  
//-------------------------------------------------------
// What this function does in short:
// 1. Gather files list form directory.
// 2. Filter files list with mesh filename regex.
// 3. Sort in alphabetic order.
// 4. Loop over selected files and read each one.
//--------------------------------------------------------
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// IMPORTANT NOTE: if there is available more than one process,
// then each process will recive one file to read.
// NOTE: if there is more processes than files,
// mesh needs to be redistributed (balancing)!
// NOTE: if there is more files than processes,
// processes will recive (in continues manner) more files to read. 
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
int utr_io_read_mesh( // returns: > 0 - number of files correctly readed; <=0 - error
					 const int Mesh_id, //IN: id of mesh to write to
					 const char * Working_dir, //IN: directory with mesh files
					 const char * Mesh_files_regex, //IN: regular expression pattern
					   // NOTE: using regex 'POSIX grep' standard  
					   const char Mesh_type //IN: mesh type character (the same for all files!)
					  )
{
  assert(Working_dir != NULL);
  assert(Mesh_files_regex != NULL);

  mf_check(Mesh_files_regex[0]!='\0',"Mesh file regex(%s) is invalid!",Mesh_files_regex);
 
  std::vector<string> filenames;
  typedef std::vector<string>::iterator filename_iter;
  
  // 1. Gather files list form directory.
  utr_io_gather_filenames_from_dir(Working_dir, filenames);

  // 2. Filter files list with mesh filename regex.
  utr_io_filter_filenames(filenames, Mesh_files_regex);
  
  // 3. Sort in alphabetic order.
  sort(filenames.begin(), filenames.end());
  
  // 4. Loop over selected files and read each one.
  int n_elems_readed=0,n_total_elems=0;
  const mmpt_io_fptr read_file= utr_io_get_reading_function(Mesh_type);  
  filename_iter it= filenames.begin(),
	end = filenames.end();

#ifdef PARALLEL
  const int nr_sub = pcr_nr_proc(),
	my_proc_id = pcr_my_proc_id();
  
  // It have to be at least 2 processes/subdomains.
  if( nr_sub > 1 ) {
	// Compute how many mesh files per process.
    const int mesh_file_per_proc = filenames.size() / nr_sub;
	// Compute how many mesh files left.
	// The remaining mesh files will be passed to processes with lowest id's.
    const int mesh_files_left = filenames.size() % nr_sub;
	
	// n_mesh_files > subdomain_id(=process_id)
	if( filenames.size() >= static_cast<size_t>(my_proc_id) ) {
	  
	  // Assign evenly (if possible) and continously (neighboring mesh-files at the same process)
	  // mesh-files per processes until there are no more meshes....
	  it += (my_proc_id-PCC_MASTER_PROC_ID) * mesh_file_per_proc
		// Take into account earlier distribution of mesh_files_left.
		+ std::min(my_proc_id-PCC_MASTER_PROC_ID, mesh_files_left);
	  
	  // Point 'end' to the file for next subdomain/process.
	  // (loop will end after reading required  mesh files)
	  end = it+mesh_file_per_proc+ ((my_proc_id-PCC_MASTER_PROC_ID) < mesh_files_left ? 1 : 0); 
	}
	else {
	  // ... to other processes assign NO mesh files.
	  it=end; // (this will end loop at beginning)
	}//!if	
  }//!if nr_sub>1
#endif //!PARALLEL
  
  for( ; it != end ; ++it) {
	// Make filename a valid path to file.
    it->insert(0,"/");
    it->insert(0,Working_dir);

	n_elems_readed=read_file(Mesh_id, it->c_str());
	
	assert( n_elems_readed > 0 );
	n_total_elems+=n_elems_readed;
  }  
  if(n_total_elems > 0) {
    mmr_test_mesh(Mesh_id);


    time_init();

    time_print();
  }
  assert(n_total_elems <=  mmr_get_nr_elem(Mesh_id));
  return filenames.size();
}

// ! extern "C"
#ifdef __cplusplus
}
#endif

// Internal routines (not extern "C")

//---------------------------------------------------------
// utr_io_get_reading_function - to return pointer to appropriate reading function
//  for given mesh type
//---------------------------------------------------------
mmpt_io_fptr utr_io_get_reading_function(const char Mesh_type)
{
  static bool reading_functions_initialized=false;
  static mmpt_io_fptr reading_functions[255]={NULL};

  // IMPORTANT! Registering reading functions.
  if(reading_functions_initialized == false) {
	
	reading_functions[ MMC_MOD_FEM_HYBRID_DATA ]=utr_io_read_hybrid;
	reading_functions[ MMC_MOD_FEM_TETRA_DATA  ]=utr_io_read_hybrid;
	reading_functions[ MMC_MOD_FEM_MESH_DATA   ]=utr_io_read_dat;
	reading_functions[ MMC_MOD_FEM_PRISM_DATA  ]=utr_io_read_dat;
	reading_functions[ MMC_NASTRAN_DATA        ]=utr_io_read_nas;
    reading_functions[ MMC_GRADMESH_DATA       ]=utr_io_read_jk;
	reading_functions[ MMC_BINARY_DATA         ]=utr_io_read_binary;
	reading_functions[ MMC_IN_ANSYS_DATA       ]=utr_io_read_in;
    reading_functions[ MMC_PARAVIEW_VTK_DATA   ]=utr_io_read_vtk;
	
	// notify that initialization is done
	reading_functions_initialized=true;
  }
  assert( reading_functions[static_cast<int>(Mesh_type)] != NULL );
  return reading_functions[static_cast<int>(Mesh_type)];
}

//---------------------------------------------------------
// utr_io_get_writing_function - to return pointer to appropriate reading function
//  for given mesh type
//---------------------------------------------------------
mmpt_io_fptr utr_io_get_writing_function(const char Mesh_type)
{
  static bool writing_functions_initialized=false;
  static mmpt_io_fptr writing_functions[255]={NULL};

  // IMPORTANT! Registering reading functions.
  if(writing_functions_initialized == false) {

//    writing_functions[ MMC_MOD_FEM_HYBRID_DATA ]=utr_io_write_hybrid;
//    writing_functions[ MMC_MOD_FEM_TETRA_DATA  ]=utr_io_write_hybrid;
//    writing_functions[ MMC_MOD_FEM_MESH_DATA   ]=utr_io_write_dat;
//    writing_functions[ MMC_MOD_FEM_PRISM_DATA  ]=utr_io_write_dat;
//    writing_functions[ MMC_NASTRAN_DATA        ]=utr_io_write_nas;
//    writing_functions[ MMC_GRADMESH_DATA       ]=utr_io_write_jk;
//    writing_functions[ MMC_BINARY_DATA         ]=utr_io_write_binary;
//    writing_functions[ MMC_IN_ANSYS_DATA       ]=utr_io_write_in;
    writing_functions[ MMC_PARAVIEW_VTK_DATA   ]=utr_io_write_vtk;

    // notify that initialization is done
    writing_functions_initialized=true;
  }
  assert( writing_functions[static_cast<int>(Mesh_type)] != NULL );
  return writing_functions[static_cast<int>(Mesh_type)];
}

//---------------------------------------------------------
// utr_io_gather_filenames_from_dir - to write all filenames into given vector
//---------------------------------------------------------
void utr_io_gather_filenames_from_dir(const char * Dir,
									  std::vector<string> & Filenames)
{
  using namespace  boost::filesystem;

  assert( Dir != NULL);

  Filenames.clear();
   
  try {
	path p(Dir); // this should be working directory...
	if(exists(p)) {
	  if(is_directory(p)) {
		for(directory_iterator it(p); it != directory_iterator(); ++it) {
#if(BOOST_VERSION>104600)
		  Filenames.push_back(it->path().filename().native());
#else
		  Filenames.push_back(it->path().filename());
#endif
		}//!for
	  }//!if
	}//!if
  }//!try
  catch( const filesystem_error& ex) {
	throw ex;
  } 
}

//---------------------------------------------------------
// utr_io_filter_filename - to filter filenames with given regular expression
//---------------------------------------------------------
void utr_io_filter_filenames(std::vector<string>& Filenames, // vector of filenames
							   const char * Regex // regular expression to test against
							   // during filtering
							 )
{
  using boost::regex;
  using boost::smatch;
  using boost::regex_match;

  typedef std::vector<string>::iterator iter;
  
  assert( Regex != NULL );
  assert( Filenames.size() > 0 );

  const char * r = Regex;
  if(Regex[0]=='.' && Regex[1]=='/') {
      mf_log_warn(" 'File name regex' begins with './' - stripping that from regular expression.");
      r += 2;
  }

  regex re(r);
  smatch what;

  // Find entries that DON'T MATCH and remove them.
  iter it(Filenames.begin());
  while( it != Filenames.end() ) {
	if( regex_match(*it,what,re) == false ) {
	  it = Filenames.erase(it);
	} else {
	  ++it;
	}
  }//!for 
}

int utr_io_count_string_in_file(
								std::ifstream & File,
								const string & To_find
)
{
  int starting_point = File.tellg();
  int count = std::count(std::istream_iterator<string>(File),
					   std::istream_iterator<string>(),
						 To_find);
  File.clear();
  File.seekg(starting_point);
  assert(File.good());
  return count;
}

//--------------------------------------------
//  utr_io_read_nas - to read single .nas file
//--------------------------------------------
int utr_io_read_nas(const int Mesh_id,const char* filename)
{
  const string node_str("GRID"),
	tria_str("CTRIAX"),
	quad_str("CQUADX"),
	tetra_str("CTETRA"),
	prism_str("CPRIZM");

  std::ifstream file(filename);

  assert(file.good());
  
  const int n_nodes=utr_io_count_string_in_file(file,node_str),
	n_tria=utr_io_count_string_in_file(file,tria_str),
	n_quad=utr_io_count_string_in_file(file,quad_str),
	n_tetra=utr_io_count_string_in_file(file,tetra_str),
	n_prism=utr_io_count_string_in_file(file,prism_str);

  assert(n_nodes > 0);
  assert(n_prism + n_tetra > 0);
  
  // below: tetrahedron have 6 edges
  // prismatic element have 9 edges
  // in case of expanding mesh we reserve also for already exisintg entities
  mmr_reserve(Mesh_id,
			  mmr_get_nr_node(Mesh_id) + n_nodes,
			  mmr_get_nr_edge(Mesh_id) + n_tetra*6+n_prism*9, 
			  mmr_get_nr_face(Mesh_id) + n_tria+n_quad+n_tetra*4+n_prism*5,
			  mmr_get_nr_elem(Mesh_id) + n_tetra+n_prism);

  //////// 1. Reading Nodes. ////////////////////////////// 
  // Find first node
  std::string s_tmp;
  do {
	file >> s_tmp;
  } while ( 0 != s_tmp.compare(node_str) );
  // Move back before 'node_str'
  file.seekg (file.tellg() - static_cast<std::streampos>(node_str.length()));

  double val,coords[3]={0};
  int n_readed_nodes=0,id=0;
  while(n_readed_nodes < n_nodes && file.good())
	{
	  file >> s_tmp;
	  file >> id >> coords[0] >> coords[1] >> coords[2] >> val;
	  mmr_add_node(Mesh_id,MMC_AUTO_GENERATE_ID,coords);
	  ++n_readed_nodes;
	}
  assert(n_readed_nodes == n_nodes);
  assert(file.good());
  
  ///////// 2. Reading edges - omitted. //////////////////////////////
  // In .nas file there are no edges

  //////// 3. Reading faces. //////////////////////////////
  int n_readed_tria=0, n_readed_quad=0;
  int bc(0),vertices[6]={0};
  while ( (n_readed_tria+n_readed_quad) < (n_tria+n_quad) && file.good() ) {
	file >> s_tmp;
	
	if(0 == s_tmp.compare(tria_str)) {
		file >> id;	// number - ignore it
		file >> bc;  // element group no. = bc no.
		file >> vertices[0];// fPtr->verts(0) = tmp2; // vertices no. !!! (not edges)
		file >> vertices[1]; //fPtr->verts(1) = tmp2; 
		file >> vertices[2]; //fPtr->verts(2) = tmp2; 
		std::sort(vertices, vertices+3);
		
        mmr_add_face(Mesh_id,MMC_AUTO_GENERATE_ID,MMC_TRIA,vertices,NULL,NULL,bc);
		++n_readed_tria;
	  }
	else if(0 == s_tmp.compare(quad_str)) {
		file >> val;	// number - ignore it
		file >> bc;  // element group no. = bc no.
		file >> vertices[0];// fPtr->verts(0) = tmp2; // vertices no. !!! (not edges)
		file >> vertices[1]; //fPtr->verts(1) = tmp2; 
		file >> vertices[3]; //fPtr->verts(2) = tmp2; 
		file >> vertices[2];
		
        mmr_add_face(Mesh_id,MMC_AUTO_GENERATE_ID,MMC_QUAD,vertices,NULL,NULL,bc);
		++n_readed_quad;
	  }
  } //! while - faces
  assert(file.good());
  assert(n_readed_tria == n_tria);
  assert(n_readed_quad == n_quad);
  
  
  //////// 4. Reading elements. ////////////////////////////////
  /////
  int n_readed_tetra(0),n_readed_prism(0),tmp2;

  file.seekg(0);
  
  while( (n_readed_tetra+n_readed_prism) < (n_tetra+n_prism) && file.good()) {
	file >> s_tmp;

	if(0 == s_tmp.compare(tetra_str)) {
	  file >> val;	// number - ignore it
      file >> tmp2; // element group no. = material
	  file >> vertices[0]; // vertices no. !!! (not edges)
	  file >> vertices[1]; 
	  file >> vertices[2]; 
	  file >> vertices[3]; 
	  std::sort(vertices,vertices+4);

      mmr_add_elem(Mesh_id,MMC_AUTO_GENERATE_ID,MMC_TETRA,vertices,NULL,tmp2);
	  
	  ++n_readed_tetra;
			}
	else if(0 == s_tmp.compare(prism_str))  {
	  file >> val;	// number - ignore it
      file >> tmp2; // element group material
	  file >> vertices[3]; // vertices no. !!! (not edges)
	  file >> vertices[4]; 
	  file >> vertices[5]; 
	  file >> vertices[0]; 
	  file >> vertices[1];
	  file >> vertices[2];

      mmr_add_elem(Mesh_id,MMC_AUTO_GENERATE_ID,MMC_PRISM,vertices,NULL,tmp2);
	  
	  ++n_readed_prism;
	  
	}
	
  } //! while - elements
  file.close();
  assert(n_readed_tetra == n_tetra);
  assert(n_readed_prism == n_prism);
  
  return n_readed_tetra+n_readed_prism;
}
  
//--------------------------------------------
//  utr_io_read_in - to read single .in file
//--------------------------------------------
int utr_io_read_in(const int Mesh_id,const char* filename)
{
  return 0;
}

//--------------------------------------------
//  utr_io_read_binary - to read single binary dump file
//--------------------------------------------
int utr_io_read_binary(const int Mesh_id,const char* filename)
{
  return 0;
}

  
//--------------------------------------------
//  utr_io_read_hybrid - to read single .kaz file
//--------------------------------------------
int utr_io_read_hybrid(const int Mesh_id,const char* filename)
{
  return 0;
}

//--------------------------------------------
//  utr_io_read_am - to read single .am file
//--------------------------------------------
int utr_io_read_am(const int Mesh_id,const char* filename)
{
  return 0;
}

//--------------------------------------------
//  utr_io_read_dat - to read single .dat file
//--------------------------------------------
int utr_io_read_dat(const int Mesh_id,const char* filename)
{
    std::ifstream file(filename);
    assert(file.good());

    int tmp,next_index(0), vert_count(0);
    file >> tmp >> tmp >> tmp >> tmp >> vert_count >> next_index;

    // Vertices
    mmr_reserve(Mesh_id,vert_count,0,0,0);
    int vert_readed=0;
    double coords[3]={0.0};
    while(vert_readed < vert_count)
    {
        file >> coords[0] >> coords[1] >> coords[2];
        mmr_add_node(Mesh_id,MMC_AUTO_GENERATE_ID,coords);
        ++vert_readed;
    }

    // Edges
    int edge_count=0;
    file >> edge_count >>  tmp;
    mmr_reserve(Mesh_id,0,edge_count,0,0);

    int v[6]={0},type=0,edge_readed=0;
    while(edge_readed < edge_count)
    {
        file >>  type;
        file >> v[0] >> v[1];
        //if(vertices[0] > vertices[1])
        //{
        //	std::swap(vertices[0],vertices[1]);
        //}
        mmr_add_edge(Mesh_id,MMC_AUTO_GENERATE_ID,v);
        ++edge_readed;
    }

    // Faces
    int face_count=0;
    file >> face_count >> tmp;

    mmr_reserve(Mesh_id,0,0,face_count,0);

    int face_readed=0,params[2]={0},edges[5]={0};
    bool edges_flip[4]={false};
    while((face_readed < face_count) && file.good())
    {
        file >> type;
        if(type < 2 && type > 4)
        {
            throw "\nDmpFileImporter::GetNextFace: wrong face_type.\n Make sure that file has platform-specyfic file endings (Win/Linux).";
        }
        file >> tmp;
        file >> params[0] >> params[1];

        // check negative values
        for(int i(0); i < type; ++i)
        {
            edges_flip[i]=false;
            file >> edges[i];
            edges_flip[i]=(edges[i] < 0);
            if(edges_flip[i])
            {
                edges[i]*=-1;
            }

            assert(edges[i] >= 1);
        }

        //for(register int i(0); i < face_type; ++i) {
        //    edgePtrs[i]=&m.edges_[edges[i]];
        //    assert(edgePtrs[i] != NULL);
        //}

        //std::sort(edgePtrs,edgePtrs+face_type, & Edge::comparePtrs );

        //for(register int i(0); i < face_type; ++i) {
        //	edges[i]=edgePtrs[i]->pos_;
        //}
        int ed_nodes[2]={0};
        mmr_edge_nodes(Mesh_id,edges[0],ed_nodes);
        v[0]= ed_nodes[ edges_flip[0] ? 1 : 0 ];
        v[1]= ed_nodes[ edges_flip[0] ? 0 : 1 ];
        switch(type)
        {
        case 3: // MMC_TRIA:
            {
                mmr_edge_nodes(Mesh_id,edges[1],ed_nodes);
                v[2]= ed_nodes[ edges_flip[1] ? 0 : 1 ];
                v[3]=0;
              } break;

        case 4: //MMC_QUAD:
            {
                mmr_edge_nodes(Mesh_id,edges[2],ed_nodes);
                v[2]= ed_nodes[ edges_flip[2] ? 0 : 1 ];
                v[3]= ed_nodes[ edges_flip[2] ? 1 : 0 ];
            } break;
        default: throw "Unknown face type!"; break;
        }
        mmr_add_face(Mesh_id,MMC_AUTO_GENERATE_ID,type,v,edges,params,tmp);
        ++face_readed;
    }

    int elem_count=0,elem_readed=0;
    file >> elem_count >> tmp;

    mmr_reserve(Mesh_id, 0,0,0,elem_count);
    while(elem_readed < elem_count)
    {
        int mt,rt,father,faces[5],face_fliped[5];
        file >> type;
        file >> mt >> father >> rt ;

        const int n_faces = type==MMC_TETRA? 4: 5;

        for(unsigned int i(0); i < n_faces; ++i)
        {
            file >> faces[i];
            if(faces[i] < 0) {
                face_fliped[i]=true;
                faces[i]*=-1;
            }
            else {
                face_fliped[i]=false;
            }
        }
        int vertices[6]={0},fa_nodes[5]={0};
        mmr_fa_node_coor(Mesh_id,faces[0],fa_nodes,NULL);
        switch(type)
        {
        case 7: //MMC_TETRA:
        {
            vertices[0] = fa_nodes[1+0];
            vertices[1] = fa_nodes[1+1];
            vertices[2] = fa_nodes[1+2];
            mmr_fa_node_coor(Mesh_id,faces[1],fa_nodes,NULL);
            vertices[3] = fa_nodes[1+2];
        } break;
        case 5: //MMC_PRISM:
        {
            vertices[0] = fa_nodes[1+0];
            // HACK below: to conform KB ordering of nodes faceFlip is... flipped ;)
            // see PHP_FEM.pdf and hHybridMesh.pdf for details
            vertices[1] = fa_nodes[1+(face_fliped[0]?1:2)];
            vertices[2] = fa_nodes[1+(face_fliped[0]?2:1)];
            mmr_fa_node_coor(Mesh_id,faces[1],fa_nodes,NULL);
            vertices[3] = fa_nodes[1+0];
            vertices[4] = fa_nodes[1+(face_fliped[1]?2:1)];
            vertices[5] = fa_nodes[1+(face_fliped[1]?1:2)];
        } break;
        default: {
            assert(!"DmpFileImporter::doRead unknown element type");
            throw "DmpFileImporter::doRead unknown element type";
        } break;
        }//!switch(element_type)

        mmr_add_elem(Mesh_id,MMC_AUTO_GENERATE_ID,type,vertices,faces,mt);
        ++elem_readed;
    }

    assert(elem_readed > 0);
  return elem_readed;
}

//--------------------------------------------
//  utr_io_read_vtk - to read single .vtk file
//--------------------------------------------
int utr_io_read_vtk(const int Mesh_id,const char* filename)
{
  return 0;
}

//--------------------------------------------
//  utr_io_read_jk - to read single .jk file
//--------------------------------------------
int utr_io_read_jk(const int Mesh_id,const char* filename)
{
    std::ifstream file(filename);

    assert(file.good());
    // Reading initial data.
    int mx_nodes=0, mx_edges=0, mx_faces=0, mx_elems=0;
    file >> mx_nodes >> mx_edges >> mx_faces >> mx_elems;

    int num_nod_jk=0,z_bottom=0,z_top=0,num_el_lay=0,bottom_bc=0,top_bc=0,
            n_prisms=0;
    file >> num_nod_jk >> z_bottom >> z_top >> num_el_lay >> bottom_bc >> top_bc;

    if(mx_nodes < (num_nod_jk * (num_el_lay+1))) {
        mx_nodes = num_nod_jk * (num_el_lay+1);
    }

    if(num_el_lay < 1) {
        num_el_lay = 1;
    }

    int dz = (z_top-z_bottom)/num_el_lay;

    if(mx_edges < (4*num_nod_jk*(num_el_lay+1)) ) {
        mx_edges = 4*num_nod_jk*(num_el_lay+1);
    }
    // Reserving space for all kind of nodes.
    mmr_reserve(Mesh_id,mx_nodes,mx_edges,mx_faces,mx_elems);

    // Reading vertices.
    for(int istr=1; istr <= num_nod_jk; ++istr) {
        double coords[3]={0.0};
        // Read plain x y coords.
        assert(file.good());
        file >> coords[0] >> coords[1] ;
        for(int ilayer=0; ilayer < num_el_lay; ++ilayer) {
            coords[2]=z_bottom + ilayer*dz;
            mmr_add_node(Mesh_id, istr+ilayer*num_nod_jk, coords);
        }
    }

    // read the number of stored element structures
    int num_el_jk=0;
    file >> num_el_jk;

    for(int istr=1;istr<=num_el_jk; ++istr) {
        int mat_num;
        assert(file.good());
        file >>  mat_num;
        int nr_nodes = mat_num>0 ? 3 : 4;
        mat_num = abs(mat_num);

        assert(nr_nodes == 3);

        int el_nodes[8]={0};
        assert(file.good());
        file >> el_nodes[0] >> el_nodes[1] >> el_nodes[2];

        for(int ilayer=0; ilayer<num_el_lay; ++ilayer) {
            el_nodes[3] = el_nodes[0]+ilayer*num_nod_jk;
            el_nodes[4] = el_nodes[1]+ilayer*num_nod_jk;
            el_nodes[5] = el_nodes[2]+ilayer*num_nod_jk;
            mmr_add_elem(Mesh_id,MMC_AUTO_GENERATE_ID,MMC_PRISM,el_nodes,NULL,0);
            ++n_prisms;
        }
    }

    file.close();
    return n_prisms;
}

//--------------------------------------------
//  utr_io_read_jk - to read single .jk file
//--------------------------------------------
int utr_io_write_vtk(const int Mesh_id,const char* filename)
{
    return 0;
}

#endif // _utr_io_intf_
